/**
 *                            License
 * THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS  
 * CREATIVE COMMONS PUBLIC LICENSE ("CCPL" OR "LICENSE"). 
 * THE WORK IS PROTECTED BY COPYRIGHT AND/OR OTHER APPLICABLE LAW.  
 * ANY USE OF THE WORK OTHER THAN AS AUTHORIZED UNDER THIS LICENSE OR  
 * COPYRIGHT LAW IS PROHIBITED.
 * 
 * BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HERE, YOU ACCEPT AND  
 * AGREE TO BE BOUND BY THE TERMS OF THIS LICENSE. TO THE EXTENT THIS LICENSE  
 * MAY BE CONSIDERED TO BE A CONTRACT,
 * THE LICENSOR GRANTS YOU THE RIGHTS CONTAINED 
 * HERE IN CONSIDERATION OF YOUR ACCEPTANCE OF SUCH TERMS AND CONDITIONS.
 * 
 */
package lineage.model;

import java.util.Random;

import lineage.model.map.L1Map;
import lineage.model.map.L1NullMap;
import lineage.model.map.L1WorldMap;
import lineage.util.L1Point;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * 坐标位置.
 * 
 * @version 2014年4月5日下午8:50:01
 * @author jrwz
 */
public final class L1Location extends L1Point {
    private static final Logger LOG = LoggerFactory.getLogger(L1Location.class);
    private static final Random RANDOM = new Random();
    /** 地图. */
    private L1Map map = L1NullMap.getInstance();

    public L1Location() {
    }

    public L1Location(final L1Location loc) {
        this(loc.x, loc.y, loc.map);
    }

    public L1Location(final int x, final int y, final int mapId) {
        super(x, y);
        this.setMap(mapId);
    }

    public L1Location(final int x, final int y, final L1Map map) {
        super(x, y);
        this.map = map;
    }

    public L1Location(final L1Point pt, final int mapId) {
        super(pt);
        setMap(mapId);
    }

    public L1Location(final L1Point pt, final L1Map map) {
        super(pt);
        this.map = map;
    }

    public void set(final L1Location loc) {
        map = loc.map;
        x = loc.x;
        y = loc.y;
    }

    public void set(final int x, final int y, final int mapId) {
        set(x, y);
        setMap(mapId);
    }

    public void set(final int x, final int y, final L1Map map) {
        set(x, y);
        this.map = map;
    }

    public void set(final L1Point pt, final int mapId) {
        set(pt);
        setMap(mapId);
    }

    public void set(final L1Point pt, final L1Map map) {
        set(pt);
        this.map = map;
    }

    /**
     * 取得地图.
     * 
     * @return 地图
     */
    public L1Map getMap() {
        return map;
    }

    /**
     * 取得地图编号.
     * 
     * @return 地图编号
     */
    public int getMapId() {
        return map.getMapId();
    }

    /**
     * 设置地图.
     * 
     * @param map
     *            - 要设置的地图
     */
    public void setMap(L1Map map) {
        this.map = map;
    }

    /**
     * 设置地图.
     * 
     * @param mapId
     *            - 要设置的地图编号
     */
    public void setMap(final int mapId) {
        map = L1WorldMap.getInstance().getMap(mapId);
    }

    /**
     * 取得此坐标可移动随机范围的位置.
     * 
     * @param max
     *            - 最大范围
     * @param isRandomTeleport
     *            - 是否闪避城区 盟屋区
     * @return 新坐标
     */
    public L1Location randomLocation(final int max, final boolean isRandomTeleport) {
        return randomLocation(0, max, isRandomTeleport);
    }

    /**
     * 取得随机传送坐标.
     * 
     * @param min
     *            - 最小范围
     * @param max
     *            - 最大范围
     * @param isRandomTeleport
     *            - 是否闪避盟屋与战争区域坐标
     * @return 新的Location
     */
    public L1Location randomLocation(final int min, final int max, final boolean isRandomTeleport) {
        return randomLocation(this, min, max, isRandomTeleport);
    }

    /**
     * 取得随机传送坐标.
     * 
     * @param baseLocation
     *            自己的L1Location
     * @param min
     *            - 最小范围
     * @param max
     *            - 最大范围
     * @param isRandomTeleport
     *            - 是否闪避盟屋与战争区域坐标
     * @return 新的Location
     */
    public static L1Location randomLocation(final L1Location baseLocation, int min, final int max, final boolean isRandomTeleport) {
        L1Location newLocation = null;
        try {
            if (min > max) {
                throw new IllegalArgumentException("min > max 设定异常");
            }
            if (max <= 0) {
                return new L1Location(baseLocation);
            }
            if (min < 0) {
                min = 0;
            }

            newLocation = new L1Location();
            int newX = 0;
            int newY = 0;
            final int locX = baseLocation.getX();
            final int locY = baseLocation.getY();
            // final int mapId = baseLocation.getMapId();
            final L1Map map = baseLocation.getMap();

            newLocation.setMap(map);

            int locX1 = locX - max;
            int locX2 = locX + max;
            int locY1 = locY - max;
            int locY2 = locY + max;

            // map范围
            final int mapX1 = map.getX();
            final int mapX2 = mapX1 + map.getWidth();
            final int mapY1 = map.getY();
            final int mapY2 = mapY1 + map.getHeight();

            if (locX1 < mapX1) {
                locX1 = mapX1;
            }
            if (locX2 > mapX2) {
                locX2 = mapX2;
            }
            if (locY1 < mapY1) {
                locY1 = mapY1;
            }
            if (locY2 > mapY2) {
                locY2 = mapY2;
            }

            final int diffX = locX2 - locX1; // x方向
            final int diffY = locY2 - locY1; // y方向

            int trial = 0;
            final int amax = (int) Math.pow(1 + (max * 2), 2);
            final int amin = (min == 0) ? 0 : (int) Math.pow(1 + ((min - 1) * 2), 2);
            final int trialLimit = 40 * amax / (amax - amin);

            boolean run = true;
            while (run) {
                if (trial >= trialLimit) {
                    newLocation.set(locX, locY);
                    run = false;
                    break;
                }

                trial++;
                try {
                    newX = locX1 + L1Location.RANDOM.nextInt(diffX + 1);
                    newY = locY1 + L1Location.RANDOM.nextInt(diffY + 1);
                } catch (final Exception e) {
                    newLocation.set(locX, locY);
                    run = false;
                    break;
                }

                newLocation.set(newX, newY);

                if (baseLocation.getTileLineDistance(newLocation) < min) {
                    continue;
                }
                if (isRandomTeleport) {
                    // TODO 战争区域
                    // TODO 盟屋坐标
                }
                // TODO 位置可通行则中断
            }
        } catch (final Exception e) {
            LOG.error(e.getLocalizedMessage(), e);
        }
        return newLocation;
    }

    @Override
    public boolean equals(final Object obj) {
        if (!(obj instanceof L1Location)) {
            return false;
        }
        final L1Location loc = (L1Location) obj;
        return (getMap() == loc.getMap()) && (getX() == loc.getX()) && (getY() == loc.getY());
    }

    @Override
    public int hashCode() {
        return 7 * map.getMapId() + hashCode();
    }

    @Override
    public String toString() {
        return String.format("(%d, %d) on %d", x, y, map.getMapId());
    }
}
